Lua
right Lua (лу́а, «луна») — интерпретируемый язык программирования, разработанный подразделением Tecgraf Католического университета Рио-де-Жанейро (Computer Graphics Technology Group of Pontifical Catholic University of Rio de Janeiro in Brazil). Является свободно распространяемым, с открытыми исходными текстами на языке Си. Это замечательный скриптовый язык, который позволяет вам программировать скрипты для игры, таким образом реализовывать какую-ту свою логику в Worms 3D, Worms Forts: Under Siege или Worms 4: Mayhem. Здесь будет рассказано о использовании Lua на примере Worms4 (для Worms3D срипты необходимо к тому же компилировать из *.lua в *.lub файл с помощью программы luac.exe) Смысл скриптов Итак, приступим! Сначала определимся что же такое скрипт? Понимать можно по-разному но мы будем называть это программкой, которая выполняется не операционной системой, а какой-то средой, в нашем случае это интерпретатор языка lua, встроенный в червяков. Интерпретатор - это программа, которая способна выполнять определённые программы, которые она понимает. Если это интерпретатор lua, то она выполняет наш скрипт на языке lua. Интерпретатор языка lua имеет чудесное свойство - с помощью него скрипту можно дать возможность управлять той программой, которая исполняет скрипт. То есть интерпретатор языка lua это лишь прослойка между нашим скриптом и червяками. Разработчики Worms заранее придумали, что им потребуется делать в скриптах. В частности они решили, что в их скриптах будут некоторые способы по-особому начать игру, управлять игровым процессом, изменять набор оружий у червей, делать взрывы, убивать и оживлять червей. Таким образом они "сообщили" интерпретатору, что скрипты могут делать, и всем скриптам стали доступны те возможности, которые придумали разработчики червей. С другой стороны, дав возможность скриптам так нахально что-то творить с игрой, разработчики потребовали от скриптов несколько требований, а именно наличие некоторых функций, например Initialize, которая инициализирует игровой процесс. Таким образом у скриптов есть некоторые возможности, предоставленные разработчиками, однако следует помнить, что разработчики предоставили только то, что посчитали нужным, в частности с помощью lua нельзя поменять меню в игре. В чём работать? Всё о чём мы будем далее говорить требует попыток воспроизвести это всё и понять, как всё работает. Конечно lua - код можно тестировать непосредственно в червях(об этом будет рассказано позже), однако так совершенно неудобно изучать язык, его самые простые конструкции, поэтому нужно другое решение. Для тестирования рекомендуем использовать другой lua-интерпретатор с редактором SciTE. Установка и настройка SciTE с lua интерпретатором #Скачайте редактор SciTE и интерпретатор lua #Установите SciTE. #Извлекаем содержимое второго архива в какое-либо надёжное место, желательно без пробелов в пути, например C:\lua #На рабочем столе щёлкаем правой кнопкой на иконке "Мой компьютер", выбираем в меню "Свойства", переходим на вкладку "Дополнительно", жмём по кнопке "Переменные среды", ищем в списке системных переменных строчку Path, выбираем её и жмём кнопку изменить. В текстовом поле "Значение переменной" в конце добавляем текст ";C:\lua", жмём OK. В этом же окне под списком системных переменных жмём кнопку "Создать". В текстовом поле "Имя переменной" вводим "LUA_DIR", а в текстовом поле "Значение переменной" вводим "C:\lua", жмём OK. Аналогично добавляем ещё 2 переменные LUA_PATH = "?.lua;%LUA_DIR%\?.lua;%LUA_DIR%\L_DIR\?.lua" и LUA_CPATH = "?.dll;%LUA_DIR%\?.dll;%LUA_DIR%\C_DIR\?.dll". #Скачайте файл настройки для редактора SciTE для языка lua тут и замените файл C:\Program Files\SciTE\languages\lua.properties на скаченный. #Запустите SciTE. Наберите в редакторе текст: print("Hello world!") Сохраните файл как Hello.lua, а затем нажмите "Выполнить" в меню "Tools"(или кнопку F5). Внизу появится окошко консоли, в котором будет выведен результат работы вашего первого скрипта >lua.exe "C:\Documents and Settings\You\Рабочий стол\1111.lua" Hello world! >Exit code: 0 Time: 0.24 Теперь любой код в статье, не относящийся к червякам, вы сможете тестировать прямо из редактора, набрав текст и нажав F5. Обратите внимание на то, что в статье приведены примеры с псевдокодом. К примеру там используется несуществующая фунция message. В большинстве случаев можно написать в начале скрипта: message = print Основы программирования Для понимания работы скриптов в Worms крайне желательно иметь некоторые сведения о программировании, хотя реально и так понять. Расскажем, как пишутся скрипты(точнее как их правильно писать). Программа - набор команд. Команды должны быть понятны для исполнителя(у нас это интерпретатор). Наш интерепретатор понимает только команды, записанные определённым образом. Каждая команда записывается на отдельной строке(хотя вроде можно и на разных, но удобнее так). Команды выполняются последовательно одна за другой. Порядок исполнения можно менять. Далее приведены стандартные команды(операторы): Примечание: далее по тексту есть примеры с использованием псевдокода-под этим подразумевается, что он не должен работать в червях. Это просто абстрактный код. К примеру message(a) - некоторая функция, которая каким либо образом выводит свой параметр пользователю, не стоит воспринимать это так, как будто message есть в червях! Комментарии Комментарии - один из самых полезных "инструментов" для программиста. Комментарии - это простой текст, который интерпретатор полностью игнорирует, но человек по ним может быстрее понять что делает программа. В Lua комментарии записываются двумя способами *Однострочный комментарий. После двойного знака "минус" в конце строки, например: message(3) --пример однострочного комментария. --текст после двух минусов игнорируется *Многострочный комментарий. После двойного знака "минус" и двух открывающих квадратных скобок, до двух закрывающих двойных скобок, например: message(3) -- пример многострочного комментария. текст можно записывать в несколько строк, и всё что внутри будет проигнорировано, даже если написать какие-либо операторы чтобы окончить комментарий надо поставить две скобки(см следующую строку) Операторы присваивания, типы данных и массивы Переменная - особая ячейка в памяти, где могут храниться данные. Например мы можем хранить числа, текст(строки), массивы или таблицы, как это принято называть в lua (сразу несколько значений в одной ячейке). Ячейки памяти(переменные) имеют имена, чтобы их можно было однозначно определить. Переменные можно использовать в различных выражениях-обычных математических(+ - * /), в функциях и т.д. Данные, хранимые в переменных могут иметь следующие типы: *Число Запись чисел обычная: number1=12412.124 number2=7 *Текст Для записи используются кавычки, например так: text="Привет!" Соединение двух строк в одну делается с помощью оператора две точки(псевдокод): text1="Привет," text2=" мир!" text=text1..text2 message(text) Данный код выведет Привет, мир! *Булево значение(логическое, или истина или ложь) Особый тип данных, у которого всего 2 значения - ложь(false) или правда(true). Любая операция, в которой производится проверка значения(например сравнение) возвращает именно булево значение. Примеры(псевдокод): a=2 b=4 message(a b) --равенство, выведет false message(a~=b) --неравенство, выведет true message(a>b) --больше, выведет false message(a=b) --больше или равно, выведет false message(a<=b) --меньше или равно, выведет true --Логическое отрицание message(not true) --выведет false message(not false) --выведет true --Логическое И message(true and true) --выведет true message(false and true) --выведет false message(true and false) --выведет false message(false and false) --выведет false --Логическое ИЛИ message(true or true) --выведет true message(false or true) --выведет true message(true or false) --выведет true message(false or false) --выведет false message((a>b) or (a+2) b) --выведет true, потому что хотя бы одно из условий выполнено message((a>b) and (a+2) b) --выведет false, потому что одно из условий не выполнено Как видно из примера, к булевым значениям можно применять логические операторы and, or и not. Они позволяют составлять сложные условия. *Таблица Таблицу можно описать в виде sometable={ индекс1 = значение1; индекс2 = значение2; индекс3 = значение3 <...> } при этом индексы можно не указывать, тогда им будут даваться числовые индексы по порядку. К тому же запись вида sometable={ "name1" = значение1; "name2" = значение2; "name3" = значение3 <...> } эквивалентна следующей: sometable={ name1 = значение1; name2 = значение2; name3 = значение3 <...> } *Функция Это можно объяснить так - в переменной можно кроме реальных данных хранить и какое то действие, то есть когда необходимо совершить то действие, которое описано в ней. Для полноты приведём пример(псевдокод): local somefunc somefunc = function (a,b) return math.sin(a+b) --не пугайтесь, так записывается стандартная функция sin - синус end message(somefunc(1,2)) Код примера выведет значение синуса 1+2. Обратите внимание, что somefunc - это переменная, а не функция. Сама функция является безымянной. Переменные могут иметь разную область видимости-глобальные и локальные. Локальные действуют только в пределах определённого логического куска кода, например внутри функции, цикла и т.д. Глобальные действуют на самом высоком уровне. Если мы хотим воспользоваться(получить или присвоить) значение перемнной с именем foo, то сначала переменная с именем foo ищется среди локальных, а потом среди глобальных. Создание глобальной переменной, доступной отовсюду. В случае, если переменная уже существует(глобальная или локальная), то происходит изменение значения переменной <имя переменной> = <значение> Создание переменной с ограниченной областью видимости(к примеру только в функции) local <имя переменной> = <значение> Примеры(псевдокод): a = 12 b = 17 c = a+b message© При выполнении сначала переменная a''' примет значение 12, и '''b значение 17, а зачем c''' примет значение '''a+b, то есть 12+17, поэтому message© выведет 29 d=10 g=15 message(d) message(g) function a() local d = 5 g=20 message(d) message(g) end a() message(d) message(g) При выполнении выведется сначала 10 и 15, потом 5 и 20, а потом 10 и 20, что говорит о том, что локальная и глобальная переменная не одно и то же. Локальная переменная пропадает, когда заканчивается работа функции. Есть особый вид присваивания переменных - параллельное присваивание. Оно позволяет в одной строке присвоить значения сразу нескольким переменным, к примеру можно поменять местами значения двух переменных(псевдокод): x=10 y=20 message(x,y) --выведет 10, а затем 20 x, y = y, x --параллельное присваивание message(x,y) --выведет 20, а затем 1 Этот код аналогичен следующему: x=10 y=20 message(x,y) --выведет 10, а затем 20 t = x --приходится использовать дополнительную переменную x = y y = t message(x,y) --выведет 20, а затем 1 Существует особый тип перменных - массивы или таблицы. Таблица - это набор из элементов с общим именем. Чтобы их отличать используется индекс. Таблицу в lua можно представить в виде таблицы с двумя столбцами. Пусть у нас есть таблица с именем foo Создание переменной-массива, делается как и в случае обычной переменной, однако в конце надо ставить ={} что означает создание пустого массива. Его наполнение производится после этого. Для создания массива foo, приведённого в таблице выше, можно написать следующее. local foo={} foo1=15 foo2=55 Можно создавать массив, сразу заполняя его: local foo={1=15; 2=55 } Для того, чтобы получить значение какого-либо элемента можно написать так(псевдокод): message(foo1) Иногда бывает нужно узнать длину массива, для этого надо написать #''' перед именем массива, например: local foo={} foo1=15 foo2=55 message(#foo) --выведет Чудесная возможность, которую даёт lua - это создавать так называемые ассоциативные массивы, то есть такие, где в качестве индекса выступает текстовая строка(на самом деле в качестве индекса может быть что угодно, даже другой массив, но это редко применяется). Рассмотрим пример массива car. Для создания такого массива можно написать следующее: car={} car"model"="BMW" car"speed"=350 car"price"=1000000000 или так: car={} car.model="BMW" car.speed=350 car.price=1000000000 или даже так: car={ model = "BMW"; speed = 350; price = 1000000000 } Как видно, третья запись намного удобнее. При этом на массив можно смотреть, как на некоторое описание реального объекта, в нашем случае-машины с маркой BMW и большущей ценой. Для того, чтобы получить значение какого-либо элемента, например скорости можно написать так(псевдокод): message(car"speed") ну и намного проще так: message(car.speed) Условные операторы Эти операторы позволяют исполнять часть команд по условию '''if <условие> then <последовательность команд, которая будет выполнена при выполнении условия> end if <условие> then <последовательность команд, которая будет выполнена при выполнении условия> else <последовательность команд, которая будет выполнена, если условие не выполнено> end if '''<условие 1> '''then <последовательность команд, которая будет выполнена при выполнении условия 1> elseif <условие 2> then <последовательность команд, которая будет выполнена, если прошлые условия не выполнены, и при выполнении условия 2> ... elseif <условие n> then <последовательность команд, которая будет выполнена, если прошлые условия не выполнены, при выполнении условия n> else <последовательность команд, которая будет выполнена, если ни одно условие не выполнено> end Пример(псевдокод): number=InputNumber() if number>10 then message("Число больше 10") elseif '''number 3 '''then message("Число равно 3") else message("Число меньше 11 и не равно 3") end Операторы цикла Цикл - цепочка действий, которая может повторяться несколько раз. Житейский пример: вам сказали подпрыгнуть 10 раз - это цикл, в котором повторяется одно и тоже действие - прыжок - ровно 10 раз. В языках программирования аналогично можно повторять одно и тоже действие. В качестве оператора цикла в Lua используются операторы for, while '''и '''repeat. Цикл for-do-end позволяет повторять действие определённое количество раз, он называется цикл со счётчиком(то есть в нём задана переменная, которая хранит номер повтора). Цикл for устроен просто: for <переменная-счётчик> = <начало цикла>','<конец цикла>',' <шаг> do <цепочка действий, повторяемых для всех значений переменной-счётчика из указонного диапазона> end Цепочка действий, выполняемая циклом также называется телом цикла. Например(псевдокод): for i = 1, 10 do message(i) --Выведет числа 1,2,3,4,5,6,7,8,9,10 end В этом примере показана укороченная версия цикла(не пишется шаг-он необязателен). При этом значение переменной растёт с каждым шагом ровно на 1. Можно задать шаг, как в следующем примере(псевдокод): for i = 1, 10 ,2 do message(i) --Выведет все нечётные числа 1,3,5,7,9 --11 выведено не будет, так как не попадает в заданные пределы end Существует другая разновидность циклов - циклы с условием(repeat–until и while–do-end). Данный вид цикла может повторяться пока будет выполняться или не выполняться какое-либо условие. Цикл repeat–until устроен следующим образом: repeat <цепочка действий, которая будет выполнена хотя бы 1 раз, а если выполнится условие после until, то будет повторено> until <условие продолжения> Цикл repeat–until оканчивается условием, идущим следом за until, поэтому в условии можно ссылаться на локальные переменные, описанные внутри цикла. Цикл обязательно выполняется хотя бы 1 раз, а затем проверяется условие, необходимо ли продолжать повтор цикла. Пример для repeat(псевдокод): a=0 repeat message("Введите число > 0") a=input() --Пользователь вводит число until (a<1) --Повторяем, если введено не положительное число Цикл while-do-end позволяет делать циклы, в котором тело может быть никогда не выполнено. Основное отличие от прошлого цикла в том, что условие стоит в начале цикла, на входе в него. То есть сначала проверяется, нужно ли выполнять цикл, а затем происходит выполнение и, если условие всё ещё выполняется, цикл повторяется. Цикл while-do-end устроен следующим образом: while <условие> do <цепочка действий, которая может быть не выполнена ни разу, выполняется, только если выполнено условие выше> end В качестве примера для while рассмотрим, как сделать аналогию цикла for: local <переменная-счётчик>=<начало цикла> while <переменная-счётчик> < <конец цикла> do <тело цикла> <переменная-счётчик> = <переменная-счётчик> + 1 end Для управления циклами, надо уметь выходить из них по необходимости. Выйти из цикла можно с помощью оператора break. Пример для break(псевдокод): a=0 repeat message("Введите число") a=input() --Пользователь вводит число if a 0 then break --Если введён 0, то выходим end message(a) until true --Это означает правду, то есть цикл выполняется всегда В данном примере при вводе чисел они будут отображаться обратно. Но если вводится 0, то программа завершается. Следует отметить, что тут использован так называемый бесконечный цикл(его условие выполняется всегда). Это потенциально опасные конструкции, поэтому в них всегда должно быть условие остановки цикла(здесь это равенство нулю введённого числа). Функции Функцией называют подпрограмму, которая выполняет какую-то особую задачу, может принимать различные входные данные, определяющие эту задачу и соответственно возвращать результаты своей работы. Необходимость в функциях возникает из-за: #желания сократить код #сделать его более понятным #лучше контролировать работу программы, искать ошибки Важной особенностью lua-функций является то, что они могут быть вызваны интерпретатором. Таким образом можно осуществлять обработку различных событий. К примеру таким способом интерпретатор в червяках оповещает lua скрипт о том, что ранен червь, начался ход и т.д. В Lua функция имеет следующий формат: function <имя функции>(<параметр 1>, <параметр 2>, <...>) <тело функции> return <результат> end Оператор return в функции необязателен. Его задача выйти из функции и вернуть результат. Результат может быть любым типом, который мы описали раньше. Можно писать return не указывая результата, при этом функция не будет возвращать результат. Оператор return немедленно выходит из всех циклов, условий и других конструкций и завершает работу этой функции. Вызов функции происходит в следующем виде: <имя функции>(<значение параметра 1>, <значение параметра 2>, <...>) Вызов функции возвращает её значение, то есть можно использовать в различных выражениях, например: <переменная>=<имя функции>(<значение параметра 1>, <значение параметра 2>, <...>) В самом lua уже есть несколько предопределённых функций. Они, как правило разделены на библиотеки(то есть при вызове функции sin надо указывать её библиотеку, то есть math.sin() ) и конкретная версия интерпретатора может не включать в себя некоторые из них. Список функций можно узнать на этой странице В прошлых примерах мы рассматривали псевдо-функцию message. В worms её можно реализовать так: function message(text) SetData("Text.TestComment", text) SetData("CommentaryPanel.Comment", "Text.TestComment") SendMessage("CommentaryPanel.ScriptText") end Подробней о работе этого кода мы расскажем позже, а сейчас важно понять, что тут определена функция с именем message, имеющая 1 входной параметр, выполняющая задачу вывода текста на экран в червяках. Она не возвращает результата(нет оператора return), но в ней вызываются несколько функций, которые определены интерпретатором червяков. Функция становится доступной после её определения, то есть нет смысла писать строку: message("Привет") если функция ещё не определена. Есть достаточно лёгкое для понимания, но порой сложное для реализации понятие под названием рекурсия. Рекурсия - это вызов из функции этой же функции. Вот пример(вычисляет факториал числа): function factorial(number) if (number>1) then return number*factorial(number-1) else return 1 end end message(factorial(6)) Выведет 720. Работа этого кода довольно проста. Сначала мы вызваем factorial(6), она возвращает 6*factorial(5), factorial(5) возвращает 5*factorial(4), и так далее до factorial(1), который вернёт 1. Получим, что factorial(6)=6*5*4*3*2*1=720. Программируем червяков В данном разделе мы попытаемся научить вас изменять червяков, осознавая, что делает каждое действие, таким образом вы должны научиться сами придумывать скрипты. Работа интерпретатора в червяках Напомним, интерпретатор - это программа, которая способна выполнять определённые программы, которые она понимает. Он даёт нам некоторые возможности, но и требует от нас определённого порядка работы. В червяков встроен интерпретатор Lua, но так как его первоначальной целью была возможность управлять игрой из скрипта, то соответсвенно наш интерпретатор даёт некоторые только базовые возможности, которые используются в игре. Рассмотрим, в чём же проявляются особенности интерпретатора lua в worms Функции Интерпретатор в червях предоставляет несколько функций. Рассмотрим их. = SetData(container_name, value) = Позволяет изменять содержимое xml(или xom в Worms3D) контейнера с помощью скрипта. Чтобы понять смысл этого, откройте любой xml файл из папки W4/data/tweak - там вы увидите данные в xml-разметке, которая очень похожа на html. В этих файлах хранится игровая информация: все параметры червя, настройки оружия и так далее. К тому же там есть контейнеры, которые на первый взгляд не имеют смысла, к примеру CommenteryPanel.Comment. Вот он (часть содержимого файла Local.xml): CommentaryPanel.Comment 96 Непонятно, зачем в настройках хранить текст на панели комментариев, тем более что он постоянно меняется, а к тому же поле Value пусто Оказывается xml файлы предназначены не только для хранения данных, но и для определения "общих рабочих ячеек" для совместной работы скрипта и хоста. Внутренние функции червяков работают с различными данными, находящимися в xml, а скрипт может их менять с помощью SetData. Обычно в червяках для совершения какого-либо действия надо сначала установить все необходимые контейнеры, а затем уже совершить действие. То есть SetData сама по себе ничего не делает. Не ждите мгновенного проявления результата после вызова SetData. Обратите внимание, SetData не изменяет xml файл на диске. Эта функция работает только с версией файла в памяти! Пример использования SetData: SetData("CommentaryPanel.Comment", "Hello!") Для вывода этого текста к тому же надо добавить строчку SendMessage("CommentaryPanel.ScriptText") = SendMessage(message) = Данная функция "просит" червей выполнить какое-либо действие. К примеру можно вызвать так: SendMessage("CommentaryPanel.ScriptText") Это заставит червей посмотреть, что в данный момент записано в контейнере CommentaryPanel.Comment и вывести его содержимое или содержимое контейнера с таким именем на экран в панель комментариев. Поэтому обычно для вывода текста сначала необходимо в какой-либо текстовый контейнер записать сам комментарий, затем в контейнер CommentaryPanel.Comment записать имя 1-го контейнера, а затем послать сообщение CommentaryPanel.ScriptText SetData("Text.TestComment",Text) SetData("CommentaryPanel.Comment", "Text.TestComment") SendMessage("CommentaryPanel.ScriptText") SendMessage после вызова может изменять значения различных контейнеров. Это свойство используется функцией GetData. Приведём список всех известных сообщений(все они найдены в стандартных скриптах): AI.ExecuteActions AI.PerformDefaultAITurn Camera.ShakeStart Comment.SuddenDeath Commentary.Clear Commentary.EnableDefault Commentary.NoDefault CommentaryPanel.ScriptText CommentaryPanel.TimedText DoubleDamage.Activated EFMV.End EFMV.Play Earthquake.End Explosion.Construct Game.PlaceObjects GameLogic.AboutToApplyDamage GameLogic.AboutToWaterRise GameLogic.ActivateNextWorm GameLogic.AddInventory GameLogic.ApplyDamage GameLogic.Challenge.Failure GameLogic.Challenge.Success GameLogic.ClearInventories GameLogic.CrateShower GameLogic.CreateAirstrike GameLogic.CreateCrate GameLogic.CreateNuclearEffect GameLogic.CreateRandMineFactory GameLogic.CreateRandomMine GameLogic.CreateRandomOildrum GameLogic.CreateRandomTelepad GameLogic.CreateSentryGun GameLogic.CreateTelepad GameLogic.CreateTrigger GameLogic.Draw GameLogic.DropRandomCrate GameLogic.EndTurn GameLogic.GetAllTeamsHadTurn GameLogic.IncrementInventory GameLogic.Mission.Failure GameLogic.Mission.Success GameLogic.PlaceObjects GameLogic.PlaceTelepads GameLogic.ResetCrateParameters GameLogic.ResetTriggerParams GameLogic.SetNoFallDamage GameLogic.SetSpecialWeapon GameLogic.StartMineFactory GameLogic.Turn.Ended GameLogic.Turn.Started GameLogic.Tutorial.Failure Jetpack.UpdateFuel Land.Clear Net.DisableAllInput Particle.NewEmitter RandomNumber.Get SentryGun.FireAt SentryGun.LookAt String.Substitute String.SubstituteIndirect Team.Surrender Timer.EndRetreatTimer Timer.EndTurn Timer.StartGame Timer.StartHotSeatTimer Timer.StartPostActivity Timer.StartTurn Utility.Delete Weapon.Create Weapon.Delete Weapon.DisableWeaponChange Worm.ApplyPoison WormManager.GetActiveAlliances WormManager.GetSurvivingTeam WormManager.Reinitialise WormManager.TeleportIn WormSelect.OptionSelected Wormpot.SetupModes Мы не будем отдельно заострять внимание на каждом сообщении, потому что большинство из них можно найти в стандартных скриптах и понять как и зачем их применять. = SendIntMessage(message,number) = Эта функция - аналог SendMessage, однако позволяет передавать числовой параметр. Список допустимых сообщений: = SendStringMessage(message,text) = Эта функция - аналог SendMessage, однако позволяет передавать текстовый параметр. Список сообщений: = GetData(container_name) = Обратное действие к SetData. Позволяет считать содержимое xml(или xom в Worms3D) контейнера с помощью скрипта. Чаще всего вызов GetData производится при каком-то событии, чтобы узнать что именно произошло. Понятие события вы поймёте дальше, а сейчас надо понять такой пример: function Worm_Died() DeadWorm = GetData("DeadWorm.Id") --узнаём, кто умер end При смерти червя интерпретатор вызовет событие - Worm_Died(). При этом мы можем узнать номер умершего червя. В другом, менее распространённом случае, мы сначала с помощью SendMessage "просим" червей вернуть нам какое-либо значение, а потом мы его считываем из контейнера, например: SendMessage("RandomNumber.Get") --запрашиваем случайное число local RawRand = GetData("RandomNumber.Uint") --"забираем" число из контейнера = QueryContainer(container_name) = Эта функция похожа на GetData. Она принимает на вход имя контенера из xml(или xom) файла и возвращает значение, однако QueryContainer используется немного для других целей, а именно для извлечения сложных контенерных типов данных. Вот пример таких данных: Worm.Data00 369 false false 0 0 0 0 0 0.6 3 false 3 0 0 0 0 8 0 0 0 0 0 false 0 0 false false true 0 0 255 true 0 0 0 0 false false false 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 false true false false Этот контейнер описывает червя. При извлечении этого контейнера из xml с помощью QueryContainer мы получим таблицу, содержащую все элементы xml структуры: local worm = QueryContainer("Worm.Data00") --worm - таблица message(worm.Energy) --выведет оставшуюсь жизнь червяка --поскольку worm-таблица, то можно и так: message(worm"Energy") message(worm.Armoured) --есть ли броня? = EditContainer(container_name) = Как и прошлая функция, работает с контейнерами в xml или xom, но она позволяет менять их содержимое. Эта функция возвращает два значения. Первое - "блокировка" - необходима для того, чтобы после изменения контейнера освободить его. Второе - сама таблица с контейнером, которую можно изменять. EditContainer всегда используется вместе с CloseContainer. Пример(запрет на использование оружия наводнение): lock, scheme = EditContainer("GM.SchemeData") --блокируем контейнер и начинаем редактирование scheme.FloodMystery.Crate = 0 scheme.Flood.Crate = 0 scheme.Flood.Ammo = 0 CloseContainer(lock) --разблокируем и даём червям сигнал применить изменения Как видите, обязательно после всех изменений надо закрыть контейнер и снять блокировку. = CloseContainer(lock) = = CopyContainer(from_container_name,to_container_name) = = StartTimer(timer_function,delay) = = CancelTimer(timer_handle) = Категория:Уроки